﻿// Inner Fire 游戏引擎库
// AssimpPrinter - Assimp打印器
//
// Copyright (c) 2025 by 尔西大帝. All rights reserved.
//
// 所属层级：应用层(4)
// 用于打印一个模型的信息
//
// Author: 尔西大帝
// Email: 2670613871@qq.com
// Created: 2025-03-12

#include "assimp_printer.h"
#include <kits.h>
#include <klog.h>
#include <EASTL/sort.h>

using namespace ikit;

namespace ifire::game {

AssimpPrinter::AssimpPrinter(
    const eastl::string_view& path, AssimpPrinterFlag flag) {
  Assimp::Importer importer;

  // Option: aiProcess_CalcTangentSpace | aiProcess_GenSmoothNormals
  auto scene = importer.ReadFile(
      path.data(), aiProcess_Triangulate | aiProcess_LimitBoneWeights |
                       aiProcess_ConvertToLeftHanded);
  if (scene == nullptr){
    KLOG("Load %s failed!", path.data());
    return;
  }
  Parse(scene, flag);
}

AssimpPrinter::AssimpPrinter(const aiScene* scene, AssimpPrinterFlag flag) {
  Parse(scene, flag);
}

void AssimpPrinter::Parse(const aiScene* scene, AssimpPrinterFlag flag) {

  if (flag & AssimpPrinterFlag::MESHES) {
    ParseMeshes(scene);
  }
  if (flag & AssimpPrinterFlag::HIERARCHY) {
    ParseHierarchy(scene);
  }
  if (flag & AssimpPrinterFlag::SKELETON) {
    ParseSkeleton(scene);
  }
  if (flag & AssimpPrinterFlag::ANIMATION) {
    ParseAnimation(scene);
  }
  if (flag & AssimpPrinterFlag::BONE_INFO) {
    ParseBoneInfo();
  }
}

static void print_assimp_vector(const aiMatrix4x4& m) {
  print_tab();
  KPRINT("%f %f %f %f\n", m.a1, m.a2, m.a3, m.a4);
  print_tab();
  KPRINT("%f %f %f %f\n", m.b1, m.b2, m.b3, m.b4);
  print_tab();
  KPRINT("%f %f %f %f\n", m.c1, m.c2, m.c3, m.c4);
  print_tab();
  KPRINT("%f %f %f %f\n", m.d1, m.d2, m.d3, m.d4);
}

static void print_assimp_matrix(const aiMatrix4x4& m) {
  print_tab();
  KPRINT("%f %f %f %f\n", m.a1, m.a2, m.a3, m.a4);
  print_tab();
  KPRINT("%f %f %f %f\n", m.b1, m.b2, m.b3, m.b4);
  print_tab();
  KPRINT("%f %f %f %f\n", m.c1, m.c2, m.c3, m.c4);
  print_tab();
  KPRINT("%f %f %f %f\n", m.d1, m.d2, m.d3, m.d4);
}

int AssimpPrinter::GetBoneId(const aiBone* pBone) {
  int BoneIndex = 0;
  eastl::string BoneName(pBone->mName.C_Str());

  if (bone_name_index_map_.find(BoneName) == bone_name_index_map_.end()) {
    // Allocate an index for a new bone
    BoneIndex = (int)bone_name_index_map_.size();
    bone_name_index_map_[BoneName] = BoneIndex;
  } else {
    BoneIndex = bone_name_index_map_[BoneName];
  }

  return BoneIndex;
}

void AssimpPrinter::ParseBoneInfo() {
  // 创建一个向量来存储键值对
  eastl::vector<eastl::pair<eastl::string, unsigned int>> bone_vector(
      bone_name_index_map_.begin(), bone_name_index_map_.end());

  // 使用 eastl::sort 根据值（索引）进行排序
  
  eastl::sort(bone_vector.begin(), bone_vector.end(),
      [](const eastl::pair<eastl::string, unsigned int>& a,
          const eastl::pair<eastl::string, unsigned int>& b) -> bool {
        return a.second < b.second;
      });

  KLOG("\n*******************************************************");
  KLOG("Parsing bone info");
  KLOG("Bones number: %d", bone_vector.size());
  KLOG("*******************************************************\n");

  // 遍历排序后的向量并打印
  for (const auto& pair : bone_vector) {
    KLOG("[%d] %s", pair.second, pair.first.c_str());
  }
}

void AssimpPrinter::ParseSingleBone(int mesh_index, const aiBone* pBone) {
  KLOG("      Bone '%s': num vertices affected by this bone: %d\n",
      pBone->mName.C_Str(), pBone->mNumWeights);

  int bone_id = GetBoneId(pBone);
  //    KLOG("      Bone id %d\n", bone_id);

  print_assimp_matrix(pBone->mOffsetMatrix);

  for (unsigned int i = 0; i < pBone->mNumWeights; i++) {
    //        if (i == 0) KLOG("\n");
    const aiVertexWeight& vw = pBone->mWeights[i];
    //          KLOG("       %d: vertex id %d weight %.2f\n", i, vw.mVertexId,
    //          vw.mWeight);

    unsigned int global_vertex_id =
        mesh_base_vertex_[mesh_index] + vw.mVertexId;
    //        KLOG("Vertex id %d ", global_vertex_id);

    assert(global_vertex_id < vertex_to_bones_.size());
    vertex_to_bones_[global_vertex_id].Add(bone_id, vw.mWeight);
  }

  KLOG("\n");
}
void AssimpPrinter::ParseMeshes(const aiScene* scene) {
  KLOG("\n*******************************************************");
  KLOG("Parsing meshes");
  KLOG("Meshes number: %d", scene->mNumMeshes);
  KLOG("*******************************************************\n");

  int total_vertices = 0;
  int total_indices = 0;
  int total_bones = 0;

  mesh_base_vertex_.resize(scene->mNumMeshes);

  for (unsigned int i = 0; i < scene->mNumMeshes; i++) {
    const aiMesh* pMesh = scene->mMeshes[i];
    int num_vertices = pMesh->mNumVertices;
    int num_indices = pMesh->mNumFaces * 3;
    int num_bones = pMesh->mNumBones;
    mesh_base_vertex_[i] = total_vertices;
    KLOG("  Mesh %d '%s': vertices %d indices %d bones %d\n\n", i,
        pMesh->mName.C_Str(), num_vertices, num_indices, num_bones);
    total_vertices += num_vertices;
    total_indices += num_indices;
    total_bones += num_bones;

    vertex_to_bones_.resize(total_vertices);

    if (pMesh->HasBones()) {
      ParseMeshBones(i, pMesh);
    }

    KLOG("\n");
  }

  KLOG("\nTotal vertices %d total indices %d total bones %d\n", total_vertices,
      total_indices, total_bones);
}

void AssimpPrinter::ParseMeshBones(int mesh_index, const aiMesh* pMesh) {
  for (unsigned int i = 0; i < pMesh->mNumBones; i++) {
    ParseSingleBone(mesh_index, pMesh->mBones[i]);
  }
}

void AssimpPrinter::ParseNode(const aiNode* pNode) {
  print_tab();
  KLOG("Node: '%s', Children: %d, Meshes: %d\n", pNode->mName.C_Str(),
      pNode->mNumChildren, pNode->mNumMeshes);
  print_tab();
  KLOG("Node transformation:\n");
  print_assimp_matrix(pNode->mTransformation);

  add_tab();

  for (unsigned int i = 0; i < pNode->mNumChildren; i++) {
    KLOG("\n");
    print_tab();
    KLOG("--- %d ---\n", i);
    ParseNode(pNode->mChildren[i]);
  }

  back_tab();
}

void AssimpPrinter::ParseHierarchy(const aiScene* scene) {
  KLOG("\n*******************************************************");
  KLOG("Parsing the node hierarchy");
  KLOG("*******************************************************\n");

  reset_tab();
  ParseNode(scene->mRootNode);
}

void AssimpPrinter::ParseSkeleton(const aiScene* scene) {
  KLOG("\n*******************************************************");
  KLOG("Parsing Skeleton");
  KLOG("Number of Skeleton: %d", scene->mNumSkeletons);
  KLOG("*******************************************************\n");

  for (size_t i = 0; i < scene->mNumSkeletons; i++) {
    auto skeleton = scene->mSkeletons[i];
    KLOG("Skeleton: %s ", skeleton->mName.C_Str());
    for (size_t j = 0; j < skeleton->mNumBones; j++) {
      auto bone = skeleton->mBones[i];
      KLOG("Bone Node Name: %s", bone->mNode->mName);
      KLOG("Offset Matrix:");
      print_assimp_matrix(bone->mOffsetMatrix);
      KLOG("Local Matrix:");
      print_assimp_matrix(bone->mLocalMatrix);
    }
  }
}

void AssimpPrinter::ParseAnimation(const aiScene* scene) {
  KLOG("\n*******************************************************");
  KLOG("Parsing Animations");
  KLOG("Number of Animations: %d", scene->mNumAnimations);
  KLOG("*******************************************************\n");

  for (size_t i = 0; i < scene->mNumAnimations; i++) {
    auto anim = scene->mAnimations[i];
    KLOG("Animation: %s ", anim->mName.C_Str());
    KLOG("TicksPerSecond: %f, Channels:%d", anim->mTicksPerSecond,
        anim->mNumChannels);

    for (size_t j = 0; j < anim->mNumChannels; j++) {
      auto channel = anim->mChannels[j];
      KLOG("Channel: %s", channel->mNodeName.C_Str());

      for (size_t k = 0; k < channel->mNumPositionKeys; k++) {
        auto v = channel->mPositionKeys[k];
        KLOG("Position: %f, Interpolation: %d, Value:%f %f %f", v.mTime,
            v.mInterpolation, v.mValue.x, v.mValue.y, v.mValue.z);
      }
      for (size_t k = 0; k < channel->mNumRotationKeys; k++) {
        auto v = channel->mRotationKeys[k];
        KLOG("Rotation: %f, Interpolation: %d, Value:%f %f %f %f", v.mTime,
            v.mInterpolation, v.mValue.x, v.mValue.y, v.mValue.z, v.mValue.w);
      }
      for (size_t k = 0; k < channel->mNumScalingKeys; k++) {
        auto v = channel->mScalingKeys[k];
        KLOG("Scale: %f, Interpolation: %d, Value:%f %f %f", v.mTime,
            v.mInterpolation, v.mValue.x, v.mValue.y, v.mValue.z);
      }
    }
  }
}

} // namespace ifire::game